/**
 * 
 * @author maoanapex88@163.com Mao Anping
 */
jweb.util.WaveShaper = function(c) {
	this.threshold = -27; // dB
	this.headroom = 21; // dB
	this.context=(c?c:null);
	this.subscriberId=null;
	this.waveshaper=null;
	this.preGain=null;
	this.postGain=null;
	this.input =null;
	this.output=null;
	
	if(c) this.initComponent();
	this.start();
}
jweb.util.WaveShaper.prototype= {
	initComponent:function(){
		this.waveshaper = this.context.createWaveShaper();
		this.preGain = this.context.createGain();
		this.postGain = this.context.createGain();
		this.preGain.connect(this.waveshaper);
		this.waveshaper.connect(this.postGain);
		this.input = this.preGain;
		this.output = this.postGain;
		
		var curve = new Float32Array(65536); // FIXME: share across instances
		this.generateColortouchCurve(curve);
		this.waveshaper.curve = curve;
	},
	onMessage:function(subj, msg, data) {
		if(subj == constants.contextInited){
			if(this.context) return ;
			this.context = msg.audioContext;//refer the obj
			this.initComponent();
			window.PageBus.publish(constants.topicLog, {level:'DEBUG', content:'audioContext of WaveShaper'});
		}
	},
	start:function() {
		if(this.subscriberId==null){
			this.subscriberId = window.PageBus.subscribe('**', this, this.onMessage, null);
		}
		else{
			this.subscriberId = window.PageBus.subscribe(this.subscriberId, this, this.onMessage, null);
		}
	},
	stop:function() {
		if(this.subscriberId!=null){
			window.PageBus.unsubscribe(this.subscriberId);
			this.subscriberId=null;
		}
	},
	generateColortouchCurve:function(curve) {
		var n = 65536;
		var n2 = n / 2;
		
		for (var i = 0; i < n2; ++i) {
			x = i / n2;
			x = this.shape(x);
			
			curve[n2 + i] = x;
			curve[n2 - i - 1] = -x;
		}
		
		return curve;
	},
	shape:function(x) {
		var linearThreshold = this.dBToLinear(this.threshold);
		var linearHeadroom = this.dBToLinear(this.headroom);
		
		var maximum = 1.05 * linearHeadroom * linearThreshold;
		var kk = (maximum - linearThreshold);
		
		var sign = x < 0 ? -1 : +1;
		var absx = Math.abs(x);
		
		var shapedInput = absx < linearThreshold ? absx : linearThreshold + kk * this.e4(absx - linearThreshold, 1.0 / kk);
		shapedInput *= sign;
		
		return shapedInput;
	},
	dBToLinear:function (db) {
		return Math.pow(10.0, 0.05 * db);
	},
	e4:function (x, k){
		return 1.0 - Math.exp(-k * x);
	},
	setDrive:function(drive) {
		this.input.gain.value = drive;
		var postDrive = Math.pow(1 / drive, 0.6);
		this.output.gain.value = postDrive;
	}
}